# -*- coding: utf-8 -*-

from __future__ import division
import math
import Rule
import ThrowIn
from utility import *

class Entity:
  def __init__(self, location = None):
    self.location = location
    if location == None:
      self.location = Location()

class Player(Entity):
  diameter = .66
  __pi2 = math.pi / 2

  def __init__(self, name, location = None):
    Entity.__init__(self, location)
    self.name            = name
    self.specifications  = []     # List of Specification
    self.strategy        = None   # Strategy
    self.orientation     = None   # Direction
    self.hasTheBall      = False
    self.isPushedBack    = False

  def appendSpecification(self, spe):
    exists = False
    for s in self.specifications:
      exists |= s == spe
    if not exists:
      self.specifications.append(spe)
  
  def play(self, game):
    action = self.strategy.evaluateAction(self, game)
    for s in self.specifications:
      s.changeAction(action)
    action.apply(self, game)

  def isBetweenPlayers(self, player1, player2, minDistance = 0):
    minX = player1.location.x if player1.location.x < player2.location.x else player2.location.x
    maxX = player2.location.x if player1.location.x < player2.location.x else player1.location.x
    minY = player1.location.y if player1.location.y < player2.location.y else player2.location.y
    maxY = player2.location.y if player1.location.y < player2.location.y else player1.location.y
    if self.location.x > minX and self.location.x < maxX and self.location.y > minY and self.location.y < maxY:
      return self.location.distanceToSegment(player1.location, player2.location) > minDistance
    return False

  def __str__(self): return self.name
  def __eq__(self, p): return p != None and self.name == p.name

class Team:
  maxPlayers = 11
  nb = 0

  def __init__(self, name):
    self.name            = name
    self.players         = []
    self.roles           = []
    self.playersAndRoles = []
    self.closestPlayer   = None
    self.formation       = None
    self.isAttacking     = False
    self.score           = 0
    self.lastPass        = None

  def addPlayer(self, player, role):
    self.players.append(player)
    self.roles.append(role)
    self.playersAndRoles.append((player, role))

  def getRoleByPlayer(self, player):
    for pr in self.playersAndRoles:
      if pr[0] == player:
        return pr[1]
    return None

  def getPlayerByRole(self, role):
    for pr in self.playersAndRoles:
      if pr[1] == role:
        return pr[0]
    return None

  def getPlayerByName(self, name):
    for p in self.players:
      if p.name == name:
        return p
    return None

  def applyFormation(self):
    for p in self.players:
      p.location.locate(self.formation.getLocation(self.getRoleByPlayer(p)))

  def setStrategy(self, strategy):
    for p in self.players:
      p.strategy = strategy

  def getPlayerClosestToTheBall(self, ball):
    if self.nb == 0:
      dmin = 2147483647
      for p in self.players:
        d = p.location.distance(ball.location)
        if d < dmin:
          dmin = d
          self.closestPlayer = p

    self.nb += 1
    self.nb %= 1
    return self.closestPlayer

  def play(self, game):
    for p in self.players:
      p.isPushedBack = False
      p.play(game)
      if p.hasTheBall:
        game.ball.locateTo(p)

class Ball(Entity):
  diameter = .22
  
  def __init__(self, location):
    Entity.__init__(self, location)
    self.speed     = 0
    self.direction = None
    self.shooted   = False

  def locateTo(self, player):
    self.location.locate(player.location)
    self.location.move(player.orientation, 3)

  def shoot(self, power, direction):
    self.shooted = True
    self.speed = power / 8
    self.direction = direction

  def move(self):
    if self.shooted:
      if self.speed < 1:
        self.shooted = False
      else:
        self.speed = 29 * self.speed / 30
        self.location.move(self.direction, self.speed)

  def stop(self): self.shooted = False

class Field:
  def __init__(self, length, width):
    self.setLength(length)
    self.setWidth(width)
    self.setLineWidth(.12)
    self.initZones()

  def setLength(self, l):
    if l >= 120: self.rlength = 120
    elif l <= 90: self.rlength = 90
    else: self.rlength = l

  def setWidth(self, w):
    if w >= 90: self.rwidth = 90
    elif w <= 45: self.rwidth = 45
    else: self.rwidth = w

  def setLineWidth(self, lw):
    if lw >= .12: self.rlineWidth = .12
    elif lw <= .06: self.rlineWidth = .06
    else: self.rlineWidth = lw

  def initZones(self):
    self.detail = 1 / self.rlineWidth
    self.lineWidth = 1
    self.length = self.rlength * self.detail
    self.width  = self.rwidth  * self.detail

    self.mainBorderRect = (0, 0, self.length, self.width)
    self.middleLine     = ((self.length / 2,  0), (self.length / 2,  self.width))
    self.middleCircle   = ((self.length / 2 ,  self.width / 2), 9.15 * self.detail)
    self.penalty1Circle = ((11 * self.detail,  self.width / 2), 9.15 * self.detail)
    self.penalty2Circle = ((self.length - 11 * self.detail,  self.width / 2), 9.15 * self.detail)
    self.penaltyArea1   = (0, (self.width - 40.3 * self.detail) / 2, 16.5 * self.detail, 40.3 * self.detail)
    self.penaltyArea2   = (self.length - 16.5 * self.detail, (self.width - 40.3 * self.detail) / 2, 16.5 * self.detail, 40.3 * self.detail)
    self.endZone1       = (0, (self.width - 18.3 * self.detail) / 2, 5.5 * self.detail, 18.3 * self.detail)
    self.endZone2       = (self.length - 5.5 * self.detail, (self.width - 18.3 * self.detail) / 2, 5.5 * self.detail, 18.3 * self.detail)
    self.goal1Rect      = (-2.4 * self.detail + self.lineWidth, (self.width - 7.3 * self.detail) / 2, 2.4 * self.detail, 7.3 * self.detail)
    self.goal2Rect      = (self.length - self.lineWidth, (self.width - 7.3 * self.detail) / 2, 2.4 * self.detail, 7.3 * self.detail)
    self.goal1CenterLoc = Location(0, self.width / 2)
    self.goal2CenterLoc = Location(self.length, self.width / 2)

    self.borderLineLL = (Location(0, 0)          , Vector(1, 0)) # Length Left
    self.borderLineLR = (Location(0, self.width) , Vector(1, 0)) #        Right
    self.borderLineWL = (Location(0, 0)          , Vector(0, 1)) # Width  Left
    self.borderLineWR = (Location(self.length, 0), Vector(0, 1)) #        Right

  def isInGoalTeam1(self, entity):    return self.__isInRect(entity.location, self.goal1Rect)
  def isInGoalTeam2(self, entity):    return self.__isInRect(entity.location, self.goal2Rect)
  def isInEndZoneTeam1(self, entity): return self.__isInRect(entity.location, self.endZone1)
  def isInEndZoneTeam2(self, entity): return self.__isInRect(entity.location, self.endZone2)
  def isInPenaltyArea1(self, entity): return self.__isInRect(entity.location, self.penaltyArea1)
  def isInPenaltyArea2(self, entity): return self.__isInRect(entity.location, self.penaltyArea2)
  def isInTheField(self, entity):     return self.__isInRect(entity.location, self.mainBorderRect)
  def isInShootZone1(self, entity):   return entity.location.distance(self.goal1CenterLoc) < 130 * self.length / 600
  def isInShootZone2(self, entity):   return entity.location.distance(self.goal2CenterLoc) < 130 * self.length / 600

  def isBehindGoalTeam1(self, entity):
    return entity.location.x < 0                 \
       and entity.location.y < self.goal1Rect[1] \
       and entity.location.y > self.goal1Rect[1] + self.goal1Rect[3]

  def isBehindGoalTeam2(self, entity):
    return entity.location.x > self.length       \
       and entity.location.y < self.goal2Rect[1] \
       and entity.location.y > self.goal2Rect[1] + self.goal2Rect[3]

  def isInTouch1(self, entity):
    return entity.location.x > 0           \
       and entity.location.x < self.length \
       and entity.location.y < 0

  def isInTouch2(self, entity):
    return entity.location.x > 0           \
       and entity.location.x < self.length \
       and entity.location.y > self.width

  def __isInRect(self, location, rect):
    return location.x > rect[0]           \
       and location.x < rect[0] + rect[2] \
       and location.y > rect[1]           \
       and location.y < rect[1] + rect[3]

  def getCenterLocation(self): return Location(self.length / 2, self.width / 2)
  def getGoal1Location(self):  return self.goal1CenterLoc
  def getGoal2Location(self):  return self.goal2CenterLoc

  def evaluateTouchLocation(self, entity):
    if self.isInTouch1(entity): return Location(entity.location.x, 1)
    if self.isInTouch2(entity): return Location(entity.location.x, self.width - 1)
    if self.isBehindGoalTeam1(entity): return Location(1, entity.location.y)
    if self.isBehindGoalTeam2(entity): return Location(self.length - 1, entity.location.y)
    raise Exception("No reason to find a touch location.")

  def evaluateCornerLocation(self, entity):
    if self.isBehindGoalTeam1(entity):
      if entity.location.y < self.goal1Rect[1]:                     return Location(0, 0)
      if entity.location.y > self.goal1Rect[1] + self.goal1Rect[3]: return Location(0, self.width)
    if self.isBehindGoalTeam2(entity):
      if entity.location.y < self.goal2Rect[1]:                     return Location(self.length, 0)
      if entity.location.y > self.goal2Rect[1] + self.goal2Rect[3]: return Location(self.length, self.width)

  def evaluateSixMetersLocation(self, entity):
    if self.isBehindGoalTeam1(entity):
      return Location(6 * self.detail, self.width / 2)
    if self.isBehindGoalTeam2(entity):
      return Location(self.length - 6 * self.detail, self.width / 2)

  def evaluateClosestFieldLocation(self, entity):
    x = entity.location.x
    y = entity.location.y
    if x < 0: x = 0
    elif x > self.length: x = self.length
    if y < 0: y = 0
    elif y > self.width: y = self.width
    return Location(x, y)

  def getGoalWidth(self): return self.goal1Rect[3]

class Game:
  def __init__(self, field, team1, team2, ball):
    self.field = field
    self.team1 = team1
    self.team2 = team2
    self.ball  = ball
    self.ballOwner     = None
    self.lastBallOwner = None
    self.rules         = Rule.Game()
    self.pause         = True
    self.throwIn       = ThrowIn.ThrowIn()
    self.isThrowingIn  = False
    self.isWaitingForThrowIn = False

  def setBallOwner(self, player):
    self.lastBallOwner = self.ballOwner
    self.ballOwner = player
    if self.lastBallOwner != None: self.lastBallOwner.hasTheBall = False
    if self.ballOwner     != None: self.ballOwner.hasTheBall     = True

    if player in self.team1.players:
      self.team1.isAttacking = True
      self.team2.isAttacking = False
    elif player in self.team2.players:
      self.team1.isAttacking = False
      self.team2.isAttacking = True
    else:
      self.team1.isAttacking = False
      self.team2.isAttacking = False

  def update(self):
    if self.pause or self.isWaitingForThrowIn:
      return
    self.throwIn.apply(self)
    self.throwIn = ThrowIn.ThrowIn()
    if self.isThrowingIn and self.ballOwner == None: raise Exception("Nobody to throw-in the ball. [1]")
    if self.isThrowingIn and self.ballOwner == None: raise Exception("Nobody to throw-in the ball. [2]")

    self.team1.play(self)
    self.team2.play(self)
    self.ball.move()
    if self.isThrowingIn and self.ballOwner == None: raise Exception("Nobody to throw-in the ball. [3]")
    self.rules.areValidated(self)